╔═════════════════════════════════════╗ ║ Programming the SoundBlaster 16 DSP ║ ║ Written by Ethan Brodsky ║ ║ (ericbrodsky@psl.wisc.edu) ║ ║ Version 2.0 ║ ╚═════════════════════════════════════╝ ┌────────────┬─────────────────────────────────────────────────────────────── │ Disclaimer │ └────────────┘ This information is distributed AS IS. The author specifically disclaims responsibility for any loss of profit or any consequential, incidental, or other damages resulting from the use or misuse of this information. This information may be freely distributed in any form as long as this disclaimer remains intact. ┌──────────────┬───────────────────────────────────────────────────────────── │ Introduction │ └──────────────┘ The Sound Blaster 16 is capable of both FM and digitized sounds. Digitized sound capibilities range from 8-bit mono 5000 HZ sound all the way up to 16-bit stereo sound at 44khz. This FAQ documents programming the SB16 DSP CT1341 chip for recording and playback of digitized audio. ┌────────────────────────────────────┬─────────────────────────────────────── │ The Sound Blaster 16 DSP I/O Ports │ └────────────────────────────────────┘ The SB16's DSP chip is programming using several I/O ports at a base I/O address determined by jumper settings. On the SB16, there are 16 I/O ports which are used for FM synthesized music, mixer settings, DSP programming and CD-ROM access. Five of these ports are used in programming the DSP. They are listed below. 2x6 - DSP Reset 2xA - DSP Read 2xC - DSP Write (Command/Data), DSP write-buffer status (Bit 7) 2xE - DSP Read-buffer status (Bit 7), DSP interrupt acknowledge 2xF - DSP 16-bit interrupt acknowledge ┌───────────────────┬──────────────────────────────────────────────────────── │ Resetting the DSP │ └───────────────────┘ You have to reset the DSP before you can program it. The DSP can be reset using the following procedure: 1) Write a 1 to the reset port (2x6) 2) Wait for 3 microseconds 3) Write a 0 to the reset port (2x6) 4) Poll the read-buffer status port (2xE) until bit 7 is set 5) Poll the read data port (2xA) until you receive an AA The DSP usually takes about 100 microseconds to initialized itself. After this period of time, if the return value is not AA or there is no data at all, then the SB card may not be installed or an incorrect I/O address is being used. ┌────────────────────┬─────────────────────────────────────────────────────── │ Writing to the DSP │ └────────────────────┘ To write a byte to the SB16, the following procedure should be used: 1) Read the write-buffer status port (2xC) until bit 7 is cleared 2) Write the value to the write port (2xC) ┌─────────────────┬────────────────────────────────────────────────────────── │ Reading the DSP │ └─────────────────┘ To read a byte from the SB16, the following procedure should be used: 1) Read the read-buffer status port (2xE) until bit 7 is set 2) Read the value from the read port (2xA) ┌────────────────────────────────┬─────────────────────────────────────────── │ Programming the DMA Controller │ └────────────────────────────────┘ The DMA (Direct Memory Access) controller controls data transfers between I/O devices and memory without using the CPU. An Intel 8237 DMAC integrated circut is used to control this. An IBM compatible computer has two DMA controllers, one for 8-bit transfers and one for 16-bit transfers. The DMA controller, coupled with an external page register, is capable of transfering blocks of up 64k to the SB16. Here is information on I/O ports and register settings: (Unused ports omitted) I/O port addresses for the DMA Address and Count Registers ╔════════════╤═════════════╤═══════════════════╗ ║ Controller │ I/O address │ Function ║ ╠════════════╪═════════════╪═══════════════════╣ ║ DMA 1 │ 00 │ Channel 0 address ║ ║ 8-bit │ 01 │ Channel 0 count ║ ║ Slave │ 02 │ Channel 1 address ║ ║ │ 03 │ Channel 1 count ║ ║ │ 04 │ Channel 2 address ║ ║ │ 05 │ Channel 2 count ║ ║ │ 06 │ Channel 3 address ║ ║ │ 07 │ Channel 3 count ║ ╠════════════╪═════════════╪═══════════════════╣ ║ DMA 2 │ C0 │ Channel 4 address ║ ║ 16-bit │ C2 │ Channel 4 count ║ ║ Master │ C4 │ Channel 5 address ║ ║ │ C6 │ Channel 5 count ║ ║ │ C8 │ Channel 6 address ║ ║ │ CA │ Channel 6 count ║ ║ │ CC │ Channel 7 address ║ ║ │ CE │ Channel 7 count ║ ╚════════════╧═════════════╧═══════════════════╝ I/O port addresses for the control registers ╔═════════════╤═══════════╤══════════════════════════════╗ ║ Address │ Operation │ Function ║ ║ DMAC1 DMAC2 │ │ ║ ╠═════════════╪═══════════╪══════════════════════════════╣ ║ 0A D4 │ Write │ Write single mask register ║ ║ 0B D6 │ Write │ Write mode register ║ ║ 0C D8 │ Write │ Clear byte pointer flip-flop ║ ╚═════════════╧═══════════╧══════════════════════════════╝ I/O port addresses for lower page registers: ╔═════════╤════════════════════════════╗ ║ Address │ Function ║ ╠═════════╪════════════════════════════╣ ║ 81 │ 8-bit DMA channel 2 page ║ ║ 82 │ 8-bit DMA channel 3 page ║ ║ 83 │ 8-bit DMA channel 1 page ║ ║ 87 │ 8-bit DMA channel 0 page ║ ║ 89 │ 16-bit DMA channel 6 page ║ ║ 8A │ 16-bit DMA channel 7 page ║ ║ 8B │ 16-bit DMA channel 5 page ║ ╚═════════╧════════════════════════════╝ Mode register bit assignments ╔═══════════╤═════════════════════════════════╗ ║ Bit/Value │ Function ║ ╠═══════════╪═════════════════════════════════╣ ║ Bits 7:6 │ Mode selection bits ║ ║ 00 │ Demand mode selected ║ ║ 01 │ Single mode selected ║ ║ 10 │ Block mode selected ║ ║ 11 │ Cascade mode selected ║ ╠═══════════╪═════════════════════════════════╣ ║ Bit 5 │ Address increment/decrement bit ║ ║ 1 │ Address decrement selected ║ ║ 0 │ Address increment selected ║ ╠═══════════╪═════════════════════════════════╣ ║ Bit 4 │ Autoinitialization enable bit ║ ║ 1 │ Auto-initialized DMA selected ║ ║ 0 │ Single-cycle DMA selected ║ ╠═══════════╪═════════════════════════════════╣ ║ Bits 3:2 │ Transfer bits ║ ║ 00 │ Verify transfer ║ ║ 01 │ Write transfer (To memory) ║ ║ 10 │ Read transfer (From memory) ║ ║ 11 │ Illegal ║ ║ ** │ Ignored if bits 6 and 7 = 11 ║ ╠═══════════╪═════════════════════════════════╣ ║ Bits 1:0 │ Channel selection bits ║ ║ 00 │ Select channel 0 (4) ║ ║ 01 │ Select channel 1 (5) ║ ║ 10 │ Select channel 2 (6) ║ ║ 11 │ Select channel 3 (7) ║ ╚═══════════╧═════════════════════════════════╝ Write single mask bit assignments ╔═══════════╤══════════════════════════════════╗ ║ Bit/Value │ Function ║ ╠═══════════╪══════════════════════════════════╣ ║ Bits 7:3 │ Unused (Set to 0) ║ ╠═══════════╪══════════════════════════════════╣ ║ Bit 2 │ Set/clear mask bit ║ ║ 1 │ Set mask bit (Disable channel) ║ ║ 0 │ Clear mask bit (Enable channel) ║ ╠═══════════╪══════════════════════════════════╣ ║ Bits 1:0 │ Channel selection bits ║ ║ 00 │ Select channel 0 (4) ║ ║ 01 │ Select channel 1 (5) ║ ║ 10 │ Select channel 2 (6) ║ ║ 11 │ Select channel 3 (7) ║ ╚═══════════╧══════════════════════════════════╝ DMAC2 is used for 16-bit I/O and DMAC1 is used for 8-bit I/O. The procedure for starting a transfer is complicated, so I'll list the steps for starting the type of DMA transfers used for sound I/O: 1) Calculate the absolute linear address of your buffer LinearAddr := Seg(Ptr^)*16 + Ofs(Ptr^)); 2) Disable the sound card DMA channel by setting the appropriate mask bit Port[MaskPort] := 1 + (Channel mod 4); 3) Clear the byte pointer flip-flop Port[ClrBytePtr] := AnyValue; 4) Write the DMA mode for the transfer The mode selection bits should be set to 00 for demand mode. The address inc/dec bit should be set to 0 for address increment. The auto-initialization bit should be set appropriately. I will discuss auto-initialized DMA later. The transfer bits should be set to 10 for playback and 01 for recording. The channel select should be set to the sound card DMA channel. (-4 for 16-bit) Port[ModePort] := Mode + (Channel mod 4); Some often used modes are: 48+Channel - Single-cycle playback 58+Channel - Auto-initialized playback 44+Channel - Single-cycle recording 54+Channel - Auto-initialized recording 5) Write the offset of the buffer, low byte followed by high byte. For sixteen bit data, the offset should be in words from the start of a 128kbyte page. The easiest method for computing 16-bit parameters is to divide the linear address by two before calculating page and offset. if SixteenBit then begin BufOffset := (LinearAddr div 2) mod 65536; Port[BaseAddrPort] := Lo(BufOffset); Port[BaseAddrPort] := Hi(BufOffset); end else begin BufOffset := LinearAddr mod 65536; Port[BaseAddrPort] := Lo(BufOffset); Port[BaseAddrPort] := Hi(BufOffset); end; 6) Write the transfer length, low byte followed by high byte. For an 8-bit transfer, write the number of bytes-1. For a 16-bit transfer, write the number of words-1. Port[CountPort] := Lo(TransferLength-1); Port[CountPort] := Hi(TransferLength-1); 7) Write the buffer page to the lower page register. For addresses in conventional memory, the upper page register can be left 0. If you are transfering 16-bit data, divide the linear address by 2 before calculating the page. if SixteenBit then Port[PagePort] := (LinearAddr div 2) div 65536 else Port[PagePort] := LinearAddr div 65536; 8) Enable the sound card DMA channel by clearing the appropriate mask bit Port[MaskPort] := DMAChannel mod 4; ┌───────────────────────────┬──────────────────────────────────────────────── │ Setting the sampling rate │ └───────────────────────────┘ Unlike earlier Sound Blasters, the SB16 is programmed with actual sampling rates instead of time constants. On the SB16, the sampling rate is set using DSP commands 41h and 42h. Command 41 is used for output and 42 is used for input. The procedure for settings the sampling rate is this: 1) Write the command (41h for output rate, 42h for input rate) 2) Write the high byte of the sampling rate (56h for 22050 hz) 3) Write the low byte of the sampling rate (22h for 22050 hz) ┌─────────────────────┬────────────────────────────────────────────────────── │ Digitized sound I/O │ └─────────────────────┘ The record or play back sound, you should use the following sequence: 1) Allocate a buffer that does not cross a 64k physical page boundary 2) Install an interrupt service routine 3) Program the DMA controller for background transfer 4) Set the sampling rate 5) Write the I/O command to the DSP 6) Write the I/O transfer mode to the DSP 7) Write the block size to the DSP (Low byte/High byte) Upon interrupt when using single-cycle DMA: 1) Program DMA controller for next block 2) Program DSP for next block 3) Copy next block if double-buffering 4) Acknowledge the interrupt with the SB by reading from port 2xE for 8-bit sound or port 2xF for 16-bit sound. 5) Acknowledge the end of interrupt with the PIC by writing 20h to port 20h if sound card is on IRQ0-7 or A0 if sound card is on IRQ8-15. ┌──────────────┬───────────────────────────────────────────────────────────── │ DSP commands │ └──────────────┘ D0 - Pause 8-bit DMA mode digitized sound I/O initiated by command Cxh. Applicable to both single-cycle and auto-initialized DMA I/O. D4 - Continue 8-bit DMA mode digitized sound I/O paused using command D0. Applicable to both single-cycle and auto-initialzied DMA I/O. D5 - Pause 16-bit DMA mode digitized sound I/O initiated by command Bxh. Applicable to both single-cycle and auto-initialized DMA I/O. D6 - Continue 16-bit DMA mode digitized sound I/O paused using command D5 Applicable to both single-cycle and auto-initialized DMA I/O. D9 - Exit 16-bit auto-initialized DMA mode digitized sound I/O after the end of the current block. DA - Exit 8-bit auto-initialized DMA mode digitized sound I/O after the end of the current block. E1 - Get DSP version number. After sending this command, read back two bytes form the DSP. The first byte is the major version number and the second byte is the minor version number. A SB16 should have a DSP version of 4.00 or greater. Check this before using an SB16 specific commands. Bx - Program 16-bit DMA mode digitized sound I/O Command sequence: Command, Mode, Lo(Length-1), Hi(Length-1) Command: ╔════╤════╤════╤════╤═══════╤══════╤═══════╤════╗ ║ D7 │ D6 │ D5 │ D4 │ D3 │ D2 │ D1 │ D0 ║ ╠════╪════╪════╪════╪═══════╪══════╪═══════╪════╣ ║ 1 │ 0 │ 1 │ 1 │ A/D │ A/I │ FIFO │ 0 ║ ╚════╧════╧════╧════┼───────┼──────┼───────┼════╝ │ 0=D/A │ 0=SC │ 0=off │ │ 1=A/D │ 1=AI │ 1=on │ └───────┴──────┴───────┘ Mode: ╔════╤════╤══════════╤════════════╤════╤════╤════╤════╗ ║ D7 │ D6 │ D5 │ D4 │ D3 │ D2 │ D1 │ D0 ║ ╠════╪════╪══════════╪════════════╪════╪════╪════╪════╣ ║ 0 │ 0 │ Stereo │ Signed │ 0 │ 0 │ 0 │ 0 ║ ╚════╧════┼──────────┼────────────┼════╧════╧════╧════╝ │ 0=Mono │ 0=unsigned │ │ 1=Stereo │ 1=signed │ └──────────┴────────────┘ Cx - Program 8-bit DMA mode digitized sound I/O Same procedure as 16-bit sound I/O using command Bx ┌──────────────────────┬───────────────────────────────────────────────────── │ Auto-initialized DMA │ └──────────────────────┘ When single-cycle DMA is used, sound output stops at the end of each block. The interrupt handler can start another transfer, but there will be a break in output. This causes a click between each block, reducing sound quality. When auto-initialized DMA is used, sound output loops around at the end of the buffer. The usual method for achieving click-less output is to allocate a buffer and divide it into two blocks. Program the DMA controller with the length of the whole buffer, but program the SB16 with the length of a block. (Half of the buffer) Two interrupts will occur for each time the buffer is played, one at the middle and one at the end. The interrupt handler should copy data into the block that was just finished so it is ready when it is needed for output. To program for auto-initialized DMA, set bit 4 of the DMA mode register. Upon interrupt when using auto-initialized DMA: 1) Copy next chunk into output buffer block that just finished 4) Acknowledge the interrupt with the SB by reading from port 2xE for 8-bit sound or port 2xF for 16-bit sound. 5) Acknowledge the end of interrupt with the PIC by writing 20h to port 20h if sound card is on IRQ0-7 or A0 if sound card is on IRQ8-15. To stop sound when using auto-initialized DMA: 8-bit - Write DSP command DA 16-bit - Write DSP command D9 ┌────────────┬─────────────────────────────────────────────────────────────── │ References │ └────────────┘ Title: Developer Kit for the Sound Blaster Series, Second Edition. Publishers: Creative Technology Ltd. Title: Intel 486 Microcomputer Model 401 Board Technical Ref. Manual Publisher: Intel Corporation Order-number: 504366-002 Title: 8237A High Performance Programmable DMA Controller specs Publisher: Intel Corporation Order-number: 231466-005 Title: 8259A Programmable Interrupt Controller specs Publisher: Intel Corporation Order-number: 231468-003 Title: SMIX Sound System Author: Ethan Brodsky Location: x2ftp.oulu.fi /pub/msdos/programming/mxlibs/smix*.zip